package aceim.protocol.snuk182.xmpp.common;
import java.util.HashMap;
import java.util.Map;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.provider.PrivacyProvider;
import org.jivesoftware.smack.provider.ProviderManager;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.GroupChatInvitation;
import org.jivesoftware.smackx.PrivateDataManager;
import org.jivesoftware.smackx.bytestreams.ibb.provider.CloseIQProvider;
import org.jivesoftware.smackx.bytestreams.ibb.provider.DataPacketProvider;
import org.jivesoftware.smackx.bytestreams.ibb.provider.OpenIQProvider;
import org.jivesoftware.smackx.bytestreams.socks5.provider.BytestreamsProvider;
import org.jivesoftware.smackx.packet.ChatStateExtension;
import org.jivesoftware.smackx.packet.LastActivity;
import org.jivesoftware.smackx.packet.OfflineMessageInfo;
import org.jivesoftware.smackx.packet.OfflineMessageRequest;
import org.jivesoftware.smackx.packet.SharedGroupsInfo;
import org.jivesoftware.smackx.provider.AdHocCommandDataProvider;
import org.jivesoftware.smackx.provider.DataFormProvider;
import org.jivesoftware.smackx.provider.DelayInformationProvider;
import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
import org.jivesoftware.smackx.provider.MUCAdminProvider;
import org.jivesoftware.smackx.provider.MUCOwnerProvider;
import org.jivesoftware.smackx.provider.MUCUserProvider;
import org.jivesoftware.smackx.provider.MessageEventProvider;
import org.jivesoftware.smackx.provider.MultipleAddressesProvider;
import org.jivesoftware.smackx.provider.RosterExchangeProvider;
import org.jivesoftware.smackx.provider.StreamInitiationProvider;
import org.jivesoftware.smackx.provider.VCardProvider;
import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
import org.jivesoftware.smackx.search.UserSearch;
import aceim.api.IProtocol;
import aceim.api.dataentity.Buddy;
import aceim.api.dataentity.BuddyGroup;
import aceim.api.dataentity.ConnectionState;
import aceim.api.dataentity.FileMessage;
import aceim.api.dataentity.InputFormFeature;
import aceim.api.dataentity.ItemAction;
import aceim.api.dataentity.Message;
import aceim.api.dataentity.OnlineInfo;
import aceim.api.dataentity.ServiceMessage;
import aceim.api.dataentity.TextMessage;
import aceim.api.dataentity.tkv.TKV;
import aceim.api.service.AccountService;
import aceim.api.service.ApiConstants;
import aceim.api.service.ICoreProtocolCallback;
import aceim.api.service.ProtocolException;
import aceim.api.utils.Logger;
import aceim.api.utils.Logger.LoggerLevel;
import aceim.api.utils.Utils;
import aceim.protocol.snuk182.xmpp.common.utils.ResourceUtils;
import android.content.Context;
import android.os.Parcelable;
import android.os.RemoteException;
import android.text.TextUtils;
public abstract class XMPPCommonService extends AccountService {
protected final XMPPServiceInternal internal = new XMPPServiceInternal(this);
private XMPPEntityAdapter entityAdapter;
protected XMPPEntityAdapter initEntityAdapter() {
return new XMPPEntityAdapter();
}
public XMPPCommonService(byte serviceId, String protocolUid, ICoreProtocolCallback callback, Context context) {
super(serviceId, protocolUid, callback, context);
}
public XMPPEntityAdapter getEntityAdapter() {
if (entityAdapter == null) {
entityAdapter = initEntityAdapter();
}
return entityAdapter;
}
@Override
protected ConnectionState getCurrentState() {
return (internal.getConnection() != null && internal.getConnection().isConnected()) ? ConnectionState.CONNECTED : ConnectionState.DISCONNECTED;
}
@Override
public IProtocol getProtocol() {
return mProtocol;
}
@Override
protected void keepaliveRequest() {
kaRequestInternal();
}
@Override
protected void timeoutReconnect() {
closeKeepaliveThread();
internal.disconnect();
try {
internal.connect(null);
} catch (ProtocolException e) {
Logger.log(e);
}
}
private final IProtocol mProtocol = new IProtocol() {
@Override
public void requestFullInfo(String uid, boolean shortInfo) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.getBuddyInfo(uid, shortInfo);
}
@Override
public void buddyAction(ItemAction action, Buddy buddy) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.buddyAction(action, buddy);
}
@Override
public void buddyGroupAction(ItemAction action, BuddyGroup group) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.groupAction(action, group);
}
@Override
public void setFeature(String featureId, OnlineInfo info) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
doSetFeature(featureId, info);
}
@Override
public void disconnect() {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.disconnect();
}
@Override
public void connect(OnlineInfo info) {
if (getCurrentState() != ConnectionState.DISCONNECTED) {
return;
}
try {
String ping = getCoreService().requestPreference( ResourceUtils.KEY_PING_TIMEOUT);
if (ping != null){
try {
pingTimeout = Integer.parseInt(ping);
} catch (Exception e) {
Logger.log(e);
}
}
internal.connect(info);
} catch (ProtocolException e) {
Logger.log(e);
}
}
@Override
public long sendMessage(Message message) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return 0;
}
if (message instanceof FileMessage) {
return internal.sendFile((FileMessage) message);
} else if (message instanceof TextMessage) {
return internal.sendMessage((TextMessage) message);
} else {
return 0;
}
}
@Override
public void requestIcon(String ownerUid) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.loadCard(ownerUid);
}
@Override
public void messageResponse(Message message, boolean accept) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
if (message instanceof FileMessage){
internal.fileTransferResponse((FileMessage)message, accept);
} else if (message instanceof ServiceMessage) {
if (message.getContactDetail().equals(getContext().getString(R.string.ask_authorization))) {
internal.authorizationResponse(message.getContactUid(), accept);
} else if (message.getContactDetail().equals(getContext().getString(R.string.confirm_destroy_room)) && accept) {
internal.destroyChatRoom(message.getContactUid());
}
}
}
@Override
public void cancelFileFransfer(long messageId) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.cancelTransfer(messageId);
}
@Override
public void sendTypingNotification(String ownerUid) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.sendTyping(ownerUid);
}
@Override
public void uploadAccountPhoto(String filePath) {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.uploadIcon(Utils.scaleAccountIcon(filePath, 120));
}
@Override
public void removeAccountPhoto() {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
internal.uploadIcon(null);
}
@Override
public void joinChatRoom(String chatId, boolean getOccupantsIcons) {
InputFormFeature feature = ResourceUtils.getAddGroupchatFeature(getContext());
for (TKV tkv : feature.getEditorFields()) {
if (tkv.getKey().equals(getContext().getString(R.string.host))) {
tkv.setValue(StringUtils.parseServer(chatId));
} else if (tkv.getKey().equals(getContext().getString(R.string.chat_room_id))) {
tkv.setValue(StringUtils.parseName(chatId));
} else if (tkv.getKey().equals(getContext().getString(R.string.nickname))) {
tkv.setValue(internal.getOnlineInfo().getName());
}
}
getCoreService().showFeatureInputForm(chatId, feature);
}
@Override
public void leaveChatRoom(String chatId) {
internal.leaveChat(chatId);
}
};
private void kaRequestInternal() {
if (getCurrentState() != ConnectionState.CONNECTED) {
return;
}
try {
internal.serviceDiscoveryRequest();
resetHeartbeat();
} catch (XMPPException e) {
Logger.log(e);
}
}
protected void doSetFeature(String featureId, OnlineInfo info) {
if (featureId.equals(ApiConstants.FEATURE_STATUS)) {
internal.setStatus(info.getFeatures().getByte(ApiConstants.FEATURE_STATUS, (byte) 0));
} else if (featureId.equals(XMPPApiConstants.FEATURE_ADD_BUDDY)) {
processAddBuddyFeature(featureId, info);
} else if (featureId.equals(XMPPApiConstants.FEATURE_GROUPCHATS)) {
internal.requestAvailableGroupchats();
} else if (featureId.equals(XMPPApiConstants.FEATURE_CONFIGURE_CHAT_ROOM_ACTION)) {
internal.getChatConfigurationForm(info.getProtocolUid());
} else if (featureId.equals(XMPPApiConstants.FEATURE_CONFIGURE_CHAT_ROOM_RESULT)) {
processChatRoomResultFeature(featureId, info);
} else if (featureId.equals(XMPPApiConstants.FEATURE_ADD_GROUPCHAT)) {
processAddGroupchatFeature(featureId, info);
} else if (featureId.equals(XMPPApiConstants.FEATURE_DESTROY_CHAT_ROOM)) {
ServiceMessage message = new ServiceMessage(getServiceId(), info.getProtocolUid(), true);
message.setIncoming(true);
message.setContactDetail(getContext().getString(R.string.confirm_destroy_room, info.getProtocolUid()));
getCoreService().message(message);
}
}
private void processAddBuddyFeature(String featureId, OnlineInfo info) {
Parcelable[] p = info.getFeatures().getParcelableArray(featureId);
if (p == null || p.length < 1) {
Logger.log("No Parcelable at XMPP Add Buddy request", LoggerLevel.INFO);
return;
}
String jidKey = getContext().getString(R.string.jid);
String nicknameKey = getContext().getString(R.string.nickname);
String jid = null;
String nickname = null;
for (Parcelable pp : p) {
TKV tkv = (TKV) pp;
if (tkv.getKey().equals(nicknameKey)) {
nickname = tkv.getValue();
} else if (tkv.getKey().equals(jidKey)) {
jid = tkv.getValue();
}
}
if (TextUtils.isEmpty(jid)) {
Logger.log("Empty JID value in add buddy request", LoggerLevel.INFO);
} else {
internal.addBuddy(jid, nickname);
}
}
private void processChatRoomResultFeature(String featureId, OnlineInfo info) {
Parcelable[] p = info.getFeatures().getParcelableArray(featureId);
if (p == null || p.length < 1) {
Logger.log("No Parcelable at XMPP Add Buddy request", LoggerLevel.INFO);
return;
}
Map<String, String> values = new HashMap<String, String>();
for (Parcelable pp : p) {
TKV tkv = (TKV) pp;
values.put(tkv.getKey(), tkv.getValue());
}
internal.chatRoomConfiguration(info.getProtocolUid(), values);
}
private void processAddGroupchatFeature(String featureId, OnlineInfo info) {
Parcelable[] p = info.getFeatures().getParcelableArray(featureId);
if (p == null || p.length < 1) {
Logger.log("No Parcelable at XMPP Add Buddy request", LoggerLevel.INFO);
return;
}
String actionKey = getContext().getString(R.string.chat_action);
String hostKey = getContext().getString(R.string.host);
String chatKey = getContext().getString(R.string.chat_room_id);
String nicknameKey = getContext().getString(R.string.nickname);
String passwordKey = getContext().getString(R.string.password);
String action = null;
String host = null;
String chat = null;
String nickname = null;
String password = null;
for (Parcelable pp : p) {
TKV tkv = (TKV) pp;
if (tkv.getKey().equals(nicknameKey)) {
nickname = tkv.getValue();
} else if (tkv.getKey().equals(chatKey)) {
chat = tkv.getValue();
} else if (tkv.getKey().equals(passwordKey)) {
password = tkv.getValue();
} else if (tkv.getKey().equals(hostKey)) {
host = tkv.getValue();
} else if (tkv.getKey().equals(actionKey)) {
action = tkv.getValue();
}
}
if (TextUtils.isEmpty(host) || TextUtils.isEmpty(chat)) {
Logger.log("Empty host or chat value in add buddy request", LoggerLevel.INFO);
} else {
internal.joinChat(host, chat, nickname, password, action != null && action.equals(getContext().getString(R.string.create_new_chat)));
}
}
public void configure(ProviderManager pm) throws RemoteException {
// Version
try {
pm.addIQProvider("query", "jabber:iq:version", Class.forName("org.jivesoftware.smackx.packet.Version"));
} catch (ClassNotFoundException e) {
// Not sure what's happening here.
}
// JEP-33: Extended Stanza Addressing
pm.addExtensionProvider("addresses", "http://jabber.org/protocol/address", new MultipleAddressesProvider());
// Privacy
pm.addIQProvider("query", "jabber:iq:privacy", new PrivacyProvider());
pm.addIQProvider("command", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider());
pm.addExtensionProvider("malformed-action", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.MalformedActionError());
pm.addExtensionProvider("bad-locale", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadLocaleError());
pm.addExtensionProvider("bad-payload", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadPayloadError());
pm.addExtensionProvider("bad-sessionid", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.BadSessionIDError());
pm.addExtensionProvider("session-expired", "http://jabber.org/protocol/commands", new AdHocCommandDataProvider.SessionExpiredError());
// Private Data Storage
pm.addIQProvider("query", "jabber:iq:private", new PrivateDataManager.PrivateDataIQProvider());
// Time
try {
pm.addIQProvider("query", "jabber:iq:time", Class.forName("org.jivesoftware.smackx.packet.Time"));
} catch (ClassNotFoundException e) {
Logger.log(e);
}
// XHTML
pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im", new XHTMLExtensionProvider());
// Roster Exchange
pm.addExtensionProvider("x", "jabber:x:roster", new RosterExchangeProvider());
// Message Events
pm.addExtensionProvider("x", "jabber:x:event", new MessageEventProvider());
// Chat State
pm.addExtensionProvider("active", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("composing", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("paused", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("inactive", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
pm.addExtensionProvider("gone", "http://jabber.org/protocol/chatstates", new ChatStateExtension.Provider());
// FileTransfer
pm.addIQProvider("si", "http://jabber.org/protocol/si", new StreamInitiationProvider());
pm.addIQProvider("query", "http://jabber.org/protocol/bytestreams", new BytestreamsProvider());
pm.addIQProvider("open", "http://jabber.org/protocol/ibb", new OpenIQProvider());
pm.addIQProvider("close", "http://jabber.org/protocol/ibb", new CloseIQProvider());
pm.addExtensionProvider("data", "http://jabber.org/protocol/ibb", new DataPacketProvider());
// Group Chat Invitations
pm.addExtensionProvider("x", "jabber:x:conference", new GroupChatInvitation.Provider());
// Service Discovery # Items
pm.addIQProvider("query", "http://jabber.org/protocol/disco#items", new DiscoverItemsProvider());
// Service Discovery # Info
pm.addIQProvider("query", "http://jabber.org/protocol/disco#info", new DiscoverInfoProvider());
// Data Forms
pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
// MUC User
pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user", new MUCUserProvider());
// MUC Admin
pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin", new MUCAdminProvider());
// MUC Owner
pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner", new MUCOwnerProvider());
// Delayed Delivery
pm.addExtensionProvider("x", "jabber:x:delay", new DelayInformationProvider());
// VCard
pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
// Offline Message Requests
pm.addIQProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageRequest.Provider());
// Offline Message Indicator
pm.addExtensionProvider("offline", "http://jabber.org/protocol/offline", new OfflineMessageInfo.Provider());
// Last Activity
pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
// User Search
pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
// SharedGroupsInfo
pm.addIQProvider("sharedgroup", "http://www.jivesoftware.org/protocol/sharedgroup", new SharedGroupsInfo.Provider());
}
}